import fs from 'fs'
import path from 'path'
import mkdirp from 'mkdirp'
import Vue from 'vue'

export class DownloadItem {
  url: string

  path: string

  name: string

  done = 0

  size = 0

  chunkNum = 0

  constructor (url: string, path: string, name: string, size: number) {
    this.url = url
    this.path = path
    this.name = name
    this.size = size
  }
}

export function download (item: DownloadItem, driver: any, progress?: (size: number) => void): Promise<{ path: string }> {
  const response = {
    'content-disposition': `attachment; filename=${encodeURIComponent(item.name)}`
  }
  return Vue.prototype.$oss.signatureUrl(item.url, { response }).then((url: string) => {
    const fn = (size: number) => {
      item.done += size
      if (progress) progress(size)
    }
    // return this.downloadWidthStream(item, url, item.localPath, fn)
    return downloadWidthSlice(url, item, driver, fn)
  })
}

export function downloadWidthSlice (url: string, item: DownloadItem, driver: any, progress?: (size: number) => void): Promise<{ path: string }> {
  return new Promise((resolve, reject) => {
    mkdirp.sync(path.resolve(item.path, '..'))

    const stream = fs.createWriteStream(item.path, { flags: (item.chunkNum || 0) > 0 ? 'a' : 'w' })
    const download = () => {
      if (driver?.status === 'error') {
        return Promise.reject(new Error('force stop'))
      }
      if (item.done >= item.size) {
        stream.close()
        resolve({ path: item.path })
      } else {
        const chunkNum = item.chunkNum || 0

        const http = Vue.prototype.$http
        http.get(url, { params: { chunkNum }, responseType: 'blob' }).then((res: any) => {
          const data = res.data as Blob
          if (data.size < 1) {
            stream.close()
            resolve({ path: item.path })
          } else {
            const toBuffer = (blob: Blob) => {
              return blob.arrayBuffer().then(r => {
                const buf = Buffer.alloc(r.byteLength)
                const view = new Uint8Array(r)
                for (let i = 0; i < buf.length; ++i) {
                  buf[i] = view[i]
                }
                return buf
              })
            }
            toBuffer(data).then(r => {
              stream.write(r, (err) => {
                if (err) {
                  stream.close()
                  reject(err)
                } else {
                  item.chunkNum = (item.chunkNum || 0) + 1
                  if (progress) progress(data.size)
                  download()
                }
              })
            })
          }
        }).catch((err: any) => {
          stream.close()
          reject(err)
        })
      }
    }

    download()
  })
}
